package com.fattyCorps.game.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fattyCorps.common.util.ByteUtils;
import com.fattyCorps.game.an.AEventListener;
import com.fattyCorps.game.an.AGlobal;
import com.fattyCorps.game.mapper.GlobalMapper;
import com.fattyCorps.game.mgr.event.EEvent;
import com.fattyCorps.game.mgr.event.EventMgr;
import com.fattyCorps.game.obj.db.DtoGlobal;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.sql.rowset.serial.SerialClob;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.*;

@Service
@Slf4j
public class GlobalService implements CommandLineRunner {
    public enum SERVER_STATE {
        STARTING,   // 启动中
        RUNNING,    // 运行中
        CLOSING,    // 关闭中
        CLOSED      // 已关闭
    }

    // 服务器是否是关闭状态
    public SERVER_STATE serverState = SERVER_STATE.STARTING;

    @Autowired
    EventMgr eventMgr;

    @Autowired
    ApplicationContext appContext;

    @Autowired
    GlobalMapper globalMapper;

    @Data
    public class GlobalBean {
        private Class cls;
        private Object bean;
    }

    private HashMap<String, GlobalBean> globalBeanMap = new HashMap<>();

    @Override
    public void run(String... args) throws Exception {
        onServerStart();
    }

    public void onServerClose() {
        serverState = SERVER_STATE.CLOSING;

        try {
            eventMgr.sendEvent(EEvent.EVENT_ON_SERVER_READY_TO_CLOSE);
            eventMgr.sendEvent(EEvent.EVENT_ON_SERVER_CLOSE_OK);
            serverState = SERVER_STATE.CLOSED;
        } catch (Exception e) {
            serverState = SERVER_STATE.RUNNING;
            throw new RuntimeException(e);
        }
    }

    private void onServerStart() {
        serverState = SERVER_STATE.STARTING;
        initGlobalFields();
        eventMgr.sendEvent(EEvent.EVENT_ON_SERVER_START);
        eventMgr.sendEvent(EEvent.EVENT_ON_CONFIG_LOAD_OK);
        serverState = SERVER_STATE.RUNNING;
    }

    // 初始化全局数据映射关系
    @SneakyThrows
    private void initGlobalFields() {
        String[] beanDefinitionNames = appContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = appContext.getBean(beanDefinitionName);
            Field[] declaredFields = bean.getClass().getDeclaredFields();

            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                AGlobal anGlobal = declaredField.getDeclaredAnnotation(AGlobal.class);
                if (anGlobal != null) {
                    GlobalBean gb = new GlobalBean();
                    gb.setCls(declaredField.getType());
                    Object fieldBean = declaredField.getType().newInstance();
                    declaredField.set(bean, fieldBean);
                    gb.setBean(fieldBean);
                    this.globalBeanMap.put(anGlobal.value(), gb);
                }
            }
        }
    }

    // 初始化全局数据
    @AEventListener(EEvent.EVENT_ON_SERVER_START)
    private void loadDbGlobal() throws SQLException {
        List<DtoGlobal> dtoGlobals = globalMapper.selectList(new QueryWrapper<>());
        for (DtoGlobal dtoGlobal : dtoGlobals) {
            GlobalBean globalBean = globalBeanMap.get(dtoGlobal.getName());
            if (globalBean != null) {
                String jsonStr;
                SerialClob sc = new SerialClob(ByteUtils.getChars(dtoGlobal.getGlobalData()));
                jsonStr = sc.getSubString(1, (int) sc.length());
                globalBean.setBean(JSONObject.parseObject(jsonStr, globalBean.getCls()));
            } else {
                log.error("loadDbGlobal error: can not find globalBean for name=" + dtoGlobal.getName());
            }
        }
    }

    // 保存全局数据
    @AEventListener(EEvent.EVENT_ON_SERVER_READY_TO_CLOSE)
    private void saveDbGlobal() throws SQLException {
        Iterator<Map.Entry<String, GlobalBean>> iterator = globalBeanMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, GlobalBean> next = iterator.next();
            GlobalBean value = next.getValue();

            byte[] bytes = JSONObject.toJSONString(value.getBean()).getBytes();
            QueryWrapper<DtoGlobal> wrapper = new QueryWrapper<>();
            wrapper.eq("name", next.getKey());
            DtoGlobal dtoGlobal = globalMapper.selectOne(wrapper);

            if (dtoGlobal == null) {
                DtoGlobal newGlobal = new DtoGlobal();
                newGlobal.setName(next.getKey());
                newGlobal.setGlobalData(bytes);
                globalMapper.insert(newGlobal);
            } else {
                dtoGlobal.setGlobalData(bytes);
                globalMapper.update(dtoGlobal, wrapper);
            }
        }
    }
}
